#include "Int_IC.h"
#include "Int_rc522.h"
#include "Dri_uart.h"
#include <string.h>
#include "Com_Util.h"

unsigned char  UID2[5] = {0x00,0x01,0x02,0x03};


/**
 * 函数名：IC_Init IC初始化
 * 功能：初始化IC卡读写模块
 * 描述：
 * 此函数主要用于IC卡读写模块的初始化过程，包括复位RC522、配置天线状态以及设置IC卡类型。
 * RC522复位确保了模块处于一个已知的稳定初始状态，天线的开关操作有助于正确配置读卡器与IC卡之间的通信环境，
 * 最后设置IC卡类型为‘A’，适用于常见的Mifare系列卡片。
 */
void IC_Init(void)
{
    // 复位RC522，确保模块处于稳定的初始状态
    PcdReset();
    
    // 关闭天线，为接下来的配置操作做准备
    PcdAntennaOff();
    
    // 开启天线，准备接收IC卡信号
    PcdAntennaOn();
    
    // 配置IC卡类型为‘A’，这是最常见的IC卡类型
    M500PcdConfigISOType('A');
}


/**
 * 读取IC卡的UID
 * 
 * 本函数尝试从IC卡读取器中读取一张IC卡的UID（唯一标识）。UID是由一系列字节组成的序列，
 * 可以用来唯一标识每一张IC卡。本函数通过调用底层的读卡器接口函数实现这一功能。
 * 
 * @param DesUid 目标UID数组的指针。如果读取操作成功，该数组将被填充以从IC卡读取器读取到的UID。
 * 
 * @return 如果成功读取到IC卡的UID，则返回true；否则返回false。
 */
bool Read_IC_Card(unsigned char  *DesUid)
{
    // 临时变量，用于存储读卡器返回的数据
    unsigned char  temp[4];
    // 用于存储UID的数组
    unsigned char  UID[5];

    // 向读卡器发送请求，0x52表示要求读取一块标签
    if (PcdRequest(0x52, temp) == MI_OK)
    {
        // 如果请求成功，尝试防止卡片冲突，即识别多张卡的情况
        if (PcdAnticoll(UID) == MI_OK)
        {
            // 如果防止卡片冲突操作成功，表明确实有一张卡在场
            strcpy((char *)DesUid, (char *)UID);
            return true;
        }
    }
    return false;
}


/**
 * @brief 比较两张IC卡的UID是否相同
 * 
 * 本函数接收两个UID，将其转换为字符指针后，利用strcmp函数进行比较。
 * 如果两个UID相同，则返回true；否则返回false。
 * 
 * @param DesUid 目标IC卡的UID，通常是从IC卡读取的UID
 * @param SrcUid 源IC卡的UID，通常是在系统中已知或期望的UID
 * @return true 如果两张IC卡的UID相同
 * @return false 如果两张IC卡的UID不同
 */
bool Cmp_IC_Card(unsigned char  *DesUid, unsigned char  *SrcUid)
{
    // 比较两个UID是否相等，相等则返回true，否则返回false
    if (!strcmp((char *)DesUid, (char *)SrcUid))
    {
        return true;
    }
    return false;
}


// /**
//  * 识别卡号运行
//  * IC_Card_Run函数用于持续检测IC卡，并根据卡的UID执行相应操作。
//  * 该函数无限循环运行，直到检测到特定按键按下为止。
//  */
// void IC_Card_Run(void)
// {
//     // 定义一个长度为5的数组用于存储IC卡的UID（用户身份标识）
//     unsigned char  UID[5];
//     // 定义一个16位无符号整数keyval，初始化为没有按键按下状态
//     // unsigned int keyval = NOKEY_PRES;
//     // 定义一个布尔变量res，用于存储读卡和比对结果
//     bool res;
    
//     // 主循环，持续检测IC卡
//     while(1)
//     {
//         // 尝试读取IC卡，如果成功，res为true
//         res = Read_IC_Card(UID);
//         if(res == true)
//         {
//             //通过串口发送数据，提示读卡成功
//             //Uart_Send_Data("读卡成功\r\n");
//             //将读取到的UID与预存的正确UID进行比对
//             res = Cmp_IC_Card(UID2, UID);
//             if(res == true)
//             {
//                 // 如果卡号匹配，通过串口发送数据，提示卡号正确
//                 // Uart_Send_Data("卡号正确\r\n");
//                 // 发出蜂鸣声，提示用户卡号验证通过
//                 // Buzz_Times(1);
//                 // 打开继电器，允许执行某些操作
//                 // JDQ = 0;
                
//                 // 延时3秒，确保继电器稳定打开
//                 // Delay1s();
//                 // Delay1s();
//                 // Delay1s();
//                 // 关闭继电器，停止操作
//                 // JDQ = 1;
//             }
//             else
//             {
//                 // 如果卡号不匹配，通过串口发送数据，提示卡号错误
//                 // Uart_Send_Data("卡号错误\r\n");
//                 // 发出蜂鸣声，提示用户卡号验证失败
//                 // Buzz_Times(3);
//             }

//         }
//         //检测按键，更新按键值
//         //keyval = key_scan();
//         // 如果检测到特定按键按下，退出循环
//         // if(keyval == KEY16_PRES)
//         //     break;
//     }
// }


/**
 * @brief 读取IC卡信息的函数
 * 
 * 本函数持续尝试读取IC卡，成功读取时将卡的UID保存，并播放提示音。
 * 如果检测到按键16被按下，则退出读卡状态。
 * 
 * @note 本函数使用了无线射频识别(RFID)技术来读取IC卡。
 */
void IC_Card_Read(void)
{
    // 定义一个长度为5的数组，用于存储IC卡的UID
    unsigned char  UID[5];
    // 定义一个无符号16位整数变量，用于存储按键值
    // unsigned int keyval = NOKEY_PRES;
    // 定义一个布尔变量，用于存储IC卡读取结果
    bool res;

    // 无限循环，等待IC卡靠近并尝试读取
    while(1)
    {
        // 尝试读取IC卡的UID
        res = Read_IC_Card(UID);
        if(res == true)
        {
            // 如果成功读取IC卡，将UID保存到另一个数组中
            strcpy((char *)UID2, (char *)UID);
            // 播放一个短促的提示音
            // Buzz_Times(1);
            // 延时1秒，避免重复读取
            Delay1s();
        }

        // 检测是否有按键按下
        // keyval = key_scan();
        // // 如果按键16被按下，退出读卡循环
        // if(keyval == KEY16_PRES)
        //     break;
    }
}

/**
 * 从指定的起始块连续读取多个数据块
 * 
 * @param startBlock 起始块号
 * @param blockCount 要读取的块数
 * @param recvData 接收数据的缓冲区
 * @param key 访问数据块所需的密钥
 * @param serNum 卡片的序列号，用于认证
 * 
 * @return 返回读取操作的状态，MI_OK表示成功，MI_ERR表示错误
 */
unsigned char IC_ReadData(unsigned char startBlock, unsigned char blockCount, unsigned char *recvData, unsigned char *key, unsigned char *serNum) {
    unsigned char status;  // 存储操作状态
    unsigned char blockAddr;  // 用于计算地址的变量
    unsigned char *dataPtr = recvData;  // 指向当前正在处理的数据位置

    // 对每个块进行处理，从起始块到起始块加上要读取的块数
    for (blockAddr = startBlock; blockAddr < (startBlock + blockCount); blockAddr++) {
        // 认证该块
        status = PcdAuthState(PICC_AUTHENT1A, blockAddr, key, serNum);
        if (status != MI_OK) {
            return MI_ERR;  // 如果认证失败，返回错误
        }

        // 读取该块的数据
        status = PcdRead(blockAddr, dataPtr);
        if (status != MI_OK) {
            return MI_ERR;  // 如果读取失败，返回错误
        }

        // 移动指针以便存储下一个块的数据
        dataPtr += 16;  // 每块16字节
    }

    return MI_OK;  // 所有块读取成功，返回OK状态
}


/**
 * @brief 对指定的存储块进行数据写入操作
 * 
 * 本函数通过使用密钥认证目标块，并将提供的数据写入到连续的存储块中
 * 主要用于对RFID卡等进行数据写入操作
 * 
 * @param startBlock 开始写入的块地址
 * @param blockCount 需要写入的块数量
 * @param writeData 指向要写入的数据的指针
 * @param key 认证使用的密钥
 * @param serNum 设备的序列号
 * 
 * @return unsigned char 返回写入状态
 *         - MI_OK: 写入成功
 *         - MI_ERR: 写入失败，可能由于认证失败或写入操作错误
 */
unsigned char IC_WriteData(unsigned char startBlock, unsigned char blockCount, unsigned char *writeData, unsigned char *key, unsigned char *serNum) {
    unsigned char status;  // 存储操作状态
    unsigned char blockAddr;  // 用于记录当前处理的块地址
    unsigned char *dataPtr = writeData;  // 指向当前正在写入的数据

    // 遍历需要写入的每一个块
    for (blockAddr = startBlock; blockAddr < (startBlock + blockCount); blockAddr++) {
        // 认证该块
        status = PcdAuthState(PICC_AUTHENT1A, blockAddr, key, serNum);
        if (status != MI_OK) {
            return MI_ERR;  // 认证失败
        }

        // 写入数据到该块
        status = PcdWrite(blockAddr, dataPtr);
        if (status != MI_OK) {
            return MI_ERR;  // 写入失败
        }

        // 移动指针以便写入下一个块的数据
        dataPtr += 16;  // 每块16字节
    }

    return MI_OK;  // 写入成功
}
